// 元数据装饰器
// npm i reflect-metadata 或 yarn add reflect-metadata
import "reflect-metadata"

// ### 在对象与对象属性上定义数据
let obj = { name: "张三", age: 18 }
Reflect.defineMetadata("objInfo", "必须是一个中国人", obj)
Reflect.defineMetadata("name", "姓名不能包含标点符号", obj, "name")
if (Reflect.hasMetadata("objInfo", obj)) {  // 判断对象上，是否有对应的元数据
  console.log(Reflect.getMetadata("objInfo", obj))  // 必须是一个中国人
  console.log(Reflect.getMetadata("name", obj, "name"))  // 姓名不能包含标点符号
}

// ### 在类中定义
// 因为是直接在类或者类上的属性上定义的元数据，所以不用加对象，与属性名。
@Reflect.metadata("peopleType", "地球人")
class Man {
  @Reflect.metadata("infoType", "姓名")
  username?: string

  @Reflect.metadata("FoodType", "一种食物")
  @Reflect.metadata("methodType", "类的方法")
  eat(food: string, foodNum: number): string { return "猪脚饭" }
}
class Father extends Man {}
if (Reflect.hasMetadata("methodType", Father.prototype, 'eat')) { console.log("Father的原型或者原型链上上有元数据methodType") }
if (!Reflect.hasOwnMetadata("methodType", Father.prototype, 'eat')) { console.log("Father的原型没有元数据methodType") }  // 判断类的原型上，是否有对应的元数据
// 获取方法上的所有元数据
// 元数据所装饰的目标的：返回值类型、所有参数类型所组成的数组、目标的类型
// [ 'design:returntype', 'design:paramtypes', 'design:type', 'methodType', 'FoodType' ]
console.log("Man上的所有元数据：", Reflect.getMetadataKeys(Man.prototype, 'eat'))
Reflect.getMetadataKeys(Father.prototype, 'eat').forEach(key => {
  console.log(`Man上的元数据${key} ---- ${Reflect.getMetadata(key, Man.prototype, 'eat')}`)
})
console.log("Father上的所有元数据：", Reflect.getOwnMetadataKeys(Father.prototype, 'eat'))  // []

// ### 在属性装饰器中，用元数据获取属性的类型
class UserService {
  username?: string = "张三"
  constructor() { console.log("创建了UserService实例") }
  showUserInfo() { console.log("姓名：", this.username) }
}

type MyPropDecorator = (targetPrototype: object, attrname: PropertyKey) => void  // 等价于PropertyDecorator
function Inject(params: any): PropertyDecorator {
  return function (targetClassPrototype, attrname) {
    let PropType = Reflect.getMetadata("design:type", targetClassPrototype, attrname)
    let propTypeInstance = new PropType()
    propTypeInstance.showUserInfo()
  }
}
class Login {
  @Inject("customer")  // 创建了UserService实例; 姓名： 张三
  customer?: UserService

  constructor() { console.log("创建了Login实例") }
}

// ### 在类装饰器中获取，方法装饰器中用元数据装饰器注入的数据

// 1、在方法装饰器中，注入元数据
type MyMethodDecorator = (targetClassPrototype: object, methodname: string, propertyDesriptor: PropertyDescriptor) => void
// type MyMethodDecorator = MethodDecorator
function get(requestPath: string): MethodDecorator {
  return (targetClassPrototype, methodname, propertyDesriptor) => {
    console.log("注入的请求路径", requestPath)
    // 注入的key、注入的value、在哪个构造函数的原型上、在哪个方法上
    Reflect.defineMetadata("path", requestPath, targetClassPrototype, methodname)
  }
}
// 2、在类装饰器中取出原数组
function Controller(routepath: string) {
  return function <T extends { new (...args: any): any }>(targetClass: T) {
    let path = ""
    Object.keys(targetClass.prototype).forEach(methodname => {
      if (Reflect.getMetadata("path", targetClass.prototype, methodname)) {
        path = Reflect.getMetadata("path", targetClass.prototype, methodname)
      }
    })
    console.log(routepath, path)
  }
}

@Controller("/user")
class UserController {
  customer?: UserService
  constructor() { console.log("创建了Login实例") }

  @get("/login")
  login() { console.log("登录成功！") }
}
export {}